package gitlet;

import javax.swing.JOptionPane;
import javax.swing.JFrame;
import javax.swing.JFileChooser;
import java.util.ArrayList;
import java.util.List;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.BufferedWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Formatter;

/**
 * 使用的工具类
 *
 * @author 王子琪、杨凯伟
 * @date 2023/11/16
 */
class Utils {

    //    final static int UID_LENGTH = 40;
    final static int HISTORY_NUM = 5;

    /**
     * 文件选择菜单
     *
     * @return {@link String}
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    static String chooseFilePath() {
        System.out.print("请选择项目地址: ");
        // 创建一个JFrame组件为parent组件
        JFrame frame = new JFrame();
        // 创建一个默认打开用户文件夹的文件选择器
        JFileChooser chooser = new JFileChooser();
        chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        chooser.showOpenDialog(frame);
        System.out.println(chooser.getSelectedFile().getAbsolutePath());
        return chooser.getSelectedFile().getAbsolutePath();
    }

    /**
     * 储存历史记录
     *
     * @param path 项目路径
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    static void saveHistory(String path) throws IOException {
        ArrayList<String> HistoryLi = readHistory();
        if (HistoryLi == null)
            HistoryLi = new ArrayList<>();
        else HistoryLi.remove(path);
        HistoryLi.add(0, path);
        File f = join(System.getProperty("user.dir"), "History");
        if (!f.exists())
            f.createNewFile();
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(f, false))) {// 没存放，存放一下
            for (int i = 0; i < HISTORY_NUM && i < HistoryLi.size(); i++)
                writer.append(HistoryLi.get(i)).append("\n");
        } catch (Exception e) {
            System.out.println("文件操作失败，再操作试试…………");
        }
    }

    /**
     * 读取历史记录
     *
     * @return {@link ArrayList}<{@link String}>
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    static ArrayList<String> readHistory() {
        File f = join(System.getProperty("user.dir"), "History");
        if (!f.exists() || f.length() == 0)
            return null;
        int i = 0;
        String HistoryPath;
        ArrayList<String> HistoryLi = new ArrayList<>();
        try (BufferedReader reader = new BufferedReader(new FileReader(f))) {
            while ((HistoryPath = reader.readLine()) != null) {
                HistoryLi.add(HistoryPath);
                if (i++ >= HISTORY_NUM)
                    break;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return HistoryLi;
    }

    /**
     * 编码读到的byte或字符串序列并返回用16进制表示的内容
     *
     * @param vals 可选变量
     * @return 16进制字符串表示的编码内容
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    static String sha1(Object... vals) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            for (Object val : vals) {
                if (val instanceof byte[]) {
                    md.update((byte[]) val);
                } else if (val instanceof String) {
                    md.update(((String) val).getBytes(StandardCharsets.UTF_8));
                } else {
                    throw new IllegalArgumentException("improper type to sha1");
                }
            }
            try (Formatter result = new Formatter()) {
                for (byte b : md.digest()) {
                    result.format("%02x", b);
                }
                return result.toString();
            }
        } catch (NoSuchAlgorithmException excp) {
            throw new IllegalArgumentException("System does not support SHA-1");
        }

    }

    /**
     * 返回用SHA-1编码的内容
     *
     * @param vals 可选变量
     * @return {@link String}
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    static String sha1(List<Object> vals) {
        return sha1(vals.toArray(new Object[0]));
    }

    /**
     * 判断文件是否在.gitlet文件夹中且不为文件夹，如果判断成功则删除
     *
     * @param file 文件
     * @return ture/false
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    static boolean restrictedDelete(File file) {
        if (!(new File(file.getParentFile(), ".gitlet")).isDirectory()) {
            throw new IllegalArgumentException("not .gitlet working directory");
        }
        if (!file.isDirectory()) {
            return file.delete();
        } else {
            return false;
        }
    }

    /**
     * 判断文件名是否在.gitlet文件夹中且不为文件夹，如果判断成功则删除
     *
     * @param file 文件
     * @return ture/false
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    static boolean restrictedDelete(String file) {
        return restrictedDelete(new File(file));
    }

    /**
     * 按byte读取指定文件内容
     *
     * @param file 文件
     * @return 文件内容的byte
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    static byte[] readContents(File file) {
        if (!file.isFile()) {
            JOptionPane.showMessageDialog(null, "发生错误: " + "must be a normal file", "警告", JOptionPane.WARNING_MESSAGE);
            throw new IllegalArgumentException("must be a normal file");
        }
        try {
            return Files.readAllBytes(file.toPath());
        } catch (IOException excp) {
            throw new IllegalArgumentException(excp.getMessage());
        }
    }

    /**
     * 将文件内容按字符串形式返回
     *
     * @param file 文件
     * @return 字符串形式的文件内容
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    static String readContentsAsString(File file) {
        return new String(readContents(file), StandardCharsets.UTF_8);
    }

    /**
     * 将contents按byte类写入file文件中(contents只能为byte或String类型)
     *
     * @param file     文件
     * @param contents 写进的内容
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    static void writeContents(File file, Object... contents) {
        try {
            if (file.isDirectory()) {
                throw new IllegalArgumentException("无法重写目录！");
                // cannot overwrite directory
            }
            BufferedOutputStream str = new BufferedOutputStream(Files.newOutputStream(file.toPath()));
            for (Object obj : contents) {
                if (obj instanceof byte[]) {
                    str.write((byte[]) obj);
                } else {
                    str.write(((String) obj).getBytes(StandardCharsets.UTF_8));
                }
            }
            str.close();
        } catch (IOException | ClassCastException excp) {
            throw new IllegalArgumentException(excp.getMessage());
        }
    }

    /**
     * 针对某个文件中指定对象的反序列化
     *
     * @param file          文件
     * @param expectedClass 指定class
     * @return 指定类对象
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    static <T extends Serializable> T readObject(File file, Class<T> expectedClass) {
        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(file))) {
            return expectedClass.cast(in.readObject());
        } catch (IOException | ClassCastException
                 | ClassNotFoundException excp) {
            throw new IllegalArgumentException(excp.getMessage());
        }
    }

    /**
     * 将实例obj按byte类写入file文件中
     *
     * @param file 文件
     * @param obj  实例
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    static void writeObject(File file, Serializable obj) {
        writeContents(file, serialize(obj));
    }

    /**
     * 查看非文件夹文件
     *
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    private static final FilenameFilter PLAIN_FILES = (dir, name) -> new File(dir, name).isFile();

    /**
     * 查看文件夹下所有文件(非文件夹)
     *
     * @param dir 指定文件夹
     * @return 文件夹下所有文件(非文件夹)的list集合
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    static List<String> plainFilenamesIn(File dir) {
        String[] files = dir.list(PLAIN_FILES);
        if (files == null) {
            return null;
        } else {
            Arrays.sort(files);
            return Arrays.asList(files);
        }
    }

    /**
     * 查看文件夹下所有文件(非文件夹)
     *
     * @param dir 指定文件夹
     * @return 文件夹下所有文件(非文件夹)的list集合
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    static List<String> plainFilenamesIn(String dir) {
        return plainFilenamesIn(new File(dir));
    }

    /**
     * 创造“first/others”的文件路径
     *
     * @param first  首地址
     * @param others 后面的地址
     * @return 文件路径 “first/others”
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    static File join(String first, String... others) {
        return Paths.get(first, others).toFile();
    }

    /**
     * 创造“first/others”的文件路径
     *
     * @param first  首地址
     * @param others 后面的地址
     * @return 文件路径 “first/others”
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    static File join(File first, String... others) {
        return Paths.get(first.getPath(), others).toFile();
    }

    /**
     * 将obj转化为字节流返回
     *
     * @param obj 实例对象
     * @return 将obj转化为的字节流
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    static byte[] serialize(Serializable obj) {
        try {
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            ObjectOutputStream objectStream = new ObjectOutputStream(stream);
            objectStream.writeObject(obj);
            objectStream.close();
            return stream.toByteArray();
        } catch (IOException excp) {
            System.out.println("Internal error serializing commit.");
        }
        return null;
    }

    /**
     * 输出帮助信息
     *
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    public static void help() {
        System.out.println("Usage:");
        System.out.println("0 param:[log] [status] [global-log] [quit]");
        System.out.println(
                "1 param:[add] [remove] [commit] [branch] [rm-branch] [checkout] [reset] [find] [merge] [remove-remote]");
        System.out.println("2 param:[checkout] [add-remote] [fetch] [push] [pull]");
    }
}
